<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>基础：10、组件 杂项</title>
	<script type="text/javascript" src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
	<div id="demo1">
		<user-profile ref="profile" msg="profile-msg"></user-profile>
	</div><br/><br/>

	<h1>多级菜单</h1>
	<div id="tree">
		<items  :model='model' v-for='model in data'></items>	
	</div>
	<template id="node">
		<li>
	        <div @click='toggle'>
	            <i v-if='isFolder' class="fa " :class="[open?'fa-folder-open':'fa-folder']"></i>
	            <!--isFolder判断是否存在子级改变图标-->
	            <i v-if='!isFolder' class="fa fa-file-text"></i> {{model.data.menuName}}
	        </div>
	        <ul v-show="open" v-if='isFolder'>
	            <items v-for='cel in model.childTreeNode' :model='cel'></items>
	        </ul>
		</li>
	</template><br/><br/>

	<h1>内联模板</h1>
	<div id="demo2">
		<inline-son msg="inline-template-test" inline-template>
			<span>2- inline-template</span>
			<!-- 
				这里虽然属于inline-son组件的模板，但不属于其作用域，
				模板编译失败，经过测试也不属于父组件的作用域
			-->
			<span>3- {{ msg }}</span>			
		</inline-son>
	</div>

	<script type="text/javascript">
		// -------------------- 编写可复用的组件 -----------------------
		// 
		// 通过前面的例子，我们了解到组件的强大之处在于其跟其他组件之间的解耦
		// 这种解耦依赖于接口的设计，层次划分明确，分工合理，只要有三部分：
		// props、events和slots:
		// (1) Props 允许外部环境传递数据给组件
		// (2) Events 允许组件触发外部环境的副作用
		// (3) Slots 允许外部环境将额外的内容组合在组件中。
		// 
		// 加上使用 v-bind 和 v-on 的简写语法，一个组件的使用类似于下面：
		// <my-component
		//   :foo="baz"
		//   :bar="qux"
		//   @event-a="doThis"
		//   @event-b="doThat"
		// >
		//   <img slot="icon" src="...">
		//   <p slot="main-text">Hello!</p>
		// </my-component>
		

		// ------------------------ 子组件索引 --------------------------
		// 需要通过父组件去访问子组件的时候，可以在子组件的实例中添加一个 ref 属性
		// 为实例子组件指定一个索引ID，通过以下形式来访问子组件：
		// var parent = new Vue({ xx })  // 父组件
		// var child = parent.$refs.refId
		// 
		// 实例demo:
		Vue.component('user-profile',{
			props: ['msg'],
			template: '<span>{{ msg }}</span>'
		})
		var parent = new Vue({
			el: '#demo1',
		})
		var child = parent.$refs.profile
		var msg = child.msg
		console.log(msg)


		// -------------------------- 异步组件 --------------------------
		// 在大型应用中，我们可能需要将应用拆分为多个小模块，按需从服务器下载。
		// 为了让事情更简单， Vue.js 允许将组件定义为一个工厂函数，动态地解析组件的定义。
		// Vue.js 只在组件需要渲染时触发工厂函数，并且把结果缓存起来，用于后面的再次渲染
		// 
		// Vue.component('async-example', function (resolve, reject) {
		//   setTimeout(function () {
		//     // Pass the component definition to the resolve callback
		//     resolve({
		//       template: '<div>I am async!</div>'
		//     })
		//   }, 1000)
		// })
		

		// -------------------------- 组件命名约定 ---------------------------
		// 在注册组件时，可以使用驼峰 camelCase 和横杠标识 kebab-case 或标题标识 TitleCase
		// 
		// 在组件定义中
		// components: {
		//   // 使用 kebab-case 形式注册
		//   'kebab-cased-component': { /* ... */ },
		//   // register using camelCase
		//   'camelCasedComponent': { /* ... */ },
		//   // register using TitleCase
		//   'TitleCasedComponent': { /* ... */ }
		// }	
		// 
		// 	在HTML模板中只能使用横杆标识 kebab-case
		//  <kebab-cased-component></kebab-cased-component>
		//  <camel-cased-component></camel-cased-component>
		//  <title-cased-component></title-cased-component>
		//  eg:
		//  <template>
		//  	<user-header></user-header>
		//  </template>
		//  
		//  使用字符串模板可以自由使用各种标识		
		//  eg:
		//  	Vue.component('father',{
		//  		template: '<son1-hmt><son2Hmt><son2Hmt></son1-hmt>'
		//  	})
		//  	
		//  如果组件未经 slot 元素传递内容，你甚至可以在组件名后使用 / 使其自闭合
		//  PS.只能在字符串模板中这样做
		

		// ---------------------------- 递归组件 -------------------------------
		// 递归组件在实际开发中使用得很频繁，比如分类好了，一个分类下面有子分类，
		// 子分类又有自己的子分类，在后台管理系统中的多级分类十分常见
		// 这个多级分类其实从数据结构上看，就是树，每个组件就是树上的一个节点
		// 
		// 定义组件
		Vue.component('items',{	        
	        props: ['model'],
	        template: '#node',
	        components: {},
	        data: function() {
	            return {
	                open: false,       // 控制是否显示子菜单
	                isFolder: true     // 控制是否存子菜单修改图标
	            }
	        },
	        computed: {
	            isFolder: function() {
	                return this.model.childTreeNode && this.model.childTreeNode.length
	            }
	        },
	        methods: {
	            toggle: function() {
	                if(this.isFolder) {
	                    this.open = !this.open
	                }
	            },
	        }			
		})

		// 树形节点数据
		var data = [{
		                "id": "1",
		                "data": {
		                    "menuName": "项目管理",
		                    "menuCode": "",
		                },
		                "childTreeNode": [{
		                    "data": {
		                        "menuName": "项目",
		                        "menuCode": "BusProject",
		                    },
		                    "childTreeNode": []
		                }, {
		                    "data": {
		                        "menuName": "我的任务",
		                        "menuCode": "BusProject",
		                    },
		                    "childTreeNode": []
		                }, {
		                    "data": {
		                        "menuName": "人员周报",
		                        "menuCode": "BusProject",
		                    },
		                    "childTreeNode": []
		                }]
		            }, {
		                "id": "2",
		                "data": {
		                    "menuName": "数据统计",
		                    "menuCode": "BusClock",
		                },
		                "childTreeNode": []
		            }, {
		                "id": "3",
		                "data": {
		                    "menuName": "人事管理",
		                    "menuCode": "",
		                },
		                "childTreeNode": []
		            }, {
		                "id": "4",
		                "data": {
		                    "menuName": "基础管理",
		                    "menuCode": "",
		                },
		                "childTreeNode": []
		            }]
		var Tree = new Vue({
			el: '#tree',
			data: {
				data: data
			}
		})


		// ------------ 组件间的循环引用Circular References Between Components ------------
		// 标题很玄，其实就是组件间的依赖问题
		// 有很多模块化的管理工具比如sea.js，require.js以及现在很火的Webpack、Browserify
		// 它们的工作原理都是把js分门别类分成各种模块，当时页面中不马上加载各个模块，只有
		// 当引用到时才引入相应的模块，各个模块之间又是有依赖关系的，比如A模块依赖于B，那么
		// 在引入A模块之前就要先引入B模块，避免依赖缺失
		// 
		// 同样的，这里可以将不同的组件看成各种模块，组件之间同样有着依赖关系，譬如一个组件
		// 会引用另外一个组件，比如A组件引用了组件B，那么在引用组件A之前必须先去引用组件B，但
		// 在一些特殊情况中组件会相互引用，比如这里组件B它也要引用组件A，造成相互引用的局面，
		// 这时模块化管理工具就不知道应该先去引用谁了？
		// 
		// 可以使用下面的方式解决，在A组件构建完成之前的beforeCreate生命周期钩子中去先注册B
		// beforeCreate: function () {
		//   this.$options.components.A = require('./A.vue')   // 这里使用了require.js
		// }
		

		// -------------------------------- 内联模板 ------------------------------------
		// 如果子组件有 inline-template 特性，组件将把它的内容当作它的模板，而不是把它当作
		// 分发内容。这让模板更灵活，如果组件有template又有 inline-template 特性呢？会发生
		// 什么，发现组件的定义模板被内联模板所取代，证明内联模板优先级更高
		Vue.component('inline-son',{
			template: '<span>1- {{ msg }}</span>',
			props: ['msg']
		})
		// 经过实例测试可知，内联模板既不属于子组件作用域，也不属于父组件作用域，任何数据模板
		// 都不会被编译，所以内联模板只适用于不带有数据模板的组件，不建议使用
		// 正规的用法还是在组件定义时声明其template属性指定的模板，或者在单独的组件定义
		// .vue文件中指定<template>声明dom模板
		new Vue({
			el: '#demo2',
			data: {
				msg: 'parent'
			}			
		})


		// -------------------------------- X-Template -----------------------------------
		// 另一种定义模版的方式是在 JavaScript 标签里使用 text/x-template 类型，并且指定一个id。
		// <script type="text/x-template" id="hello-world-template">
		//  <p>Hello hello hello</p>
		// </script>
		// 
		// Vue.component('hello-world', {
		//   template: '#hello-world-template'
		// })
		// 
		// 这同样是一种可行但不建议使用的用法，因为它将模板和组件其他定义隔离了
		

		// ------------------------- 对低开销的静态组件使用 v-once ------------------------
		// 在很前面的例子中我们使用过 v-once 指令，效果是使用了这个指令的标签的vue数据模板只
		// 会渲染一次，不会跟着数据变化动态刷新，因为 v-once 将渲染的结果缓存起来了，只会在
		// 一开始编译。因此，当组件中包含大量静态内容时，可以考虑使用 v-once 缓存渲染结果
	</script>
</body>
</html>
<!-- 代码均通过测试，有什么纰漏欢迎联系 QQ:1123176978  Wechat:hyhlovehuanong -->